package com.tengju.bff.interfaces.covercharge.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.tengju.bff.interfaces.covercharge.constant.CoverChargeEnum;
import com.tengju.bff.interfaces.covercharge.constant.ModifyTypeEnum;
import com.tengju.bff.interfaces.covercharge.constant.RevisionIsOrderEnum;
import com.tengju.bff.interfaces.covercharge.dto.*;
import com.tengju.bff.interfaces.covercharge.entity.dto.WithdrawalApplyInfo;
import com.tengju.bff.interfaces.covercharge.entity.po.*;
import com.tengju.bff.interfaces.covercharge.entity.vo.AccountRevisionApplyReqVo;
import com.tengju.bff.interfaces.covercharge.entity.vo.AdminServiceAccountQueryVo;
import com.tengju.bff.interfaces.covercharge.enums.OperateTypeEnum;
import com.tengju.bff.interfaces.covercharge.enums.ProfitTypeEnum;
import com.tengju.bff.interfaces.covercharge.enums.ServiceAccountTypeEnum;
import com.tengju.bff.interfaces.covercharge.mapper.ServiceAccountMapper;
import com.tengju.bff.interfaces.covercharge.service.*;
import com.tengju.bff.interfaces.covercharge.util.BeanMapperUtils;
import com.tengju.settlement.domain.model.account.*;
import com.tengju.settlement.domain.shared.Amount;
import com.tengju.user.domain.model.user.UserIdCode;
import com.tengju.user.domain.model.user.UserInfo;
import com.tengju.user.domain.service.UserDocService;
import com.zoi7.component.core.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author fwy
 * @since 2020-02-25
 */
@Service
@Slf4j
public class ServiceAccountServiceImpl extends ServiceImpl<ServiceAccountMapper, ServiceAccount> implements IServiceAccountService {

    @Autowired
    private IServiceCustomDetailService serviceCustomDetailService;

    @Autowired
    private IServiceManageDetailService serviceManageDetailService;

    @Autowired
    private IServiceFrozenDetailService serviceFrozenDetailService;

//    @Autowired
//    private UserInfoRepository userInfoRepository;

    @Autowired
    private UserDocService userDocService;

    @Autowired
    private UserService userService;

    @Autowired
    private IServiceProfitTypeService serviceProfitTypeService;

    @Autowired
    private AccountRepository accountRepository;

    @Autowired
    private FrozenDetailRepository frozenDetailRepository;

    /**
     * 获取收益账户
     *
     * @param accountType 账户类型
     * @param idCode      idCode
     */
    @Override
    public ServiceAccount getAccount(Integer accountType, Integer idCode) {
        ServiceAccount serviceAccountParam = new ServiceAccount();
        serviceAccountParam.setIdCode(idCode);
        ServiceAccount serviceAccount = this.getOne(new QueryWrapper<>(serviceAccountParam));
        if (serviceAccount != null) {
            return serviceAccount;
        }
        log.info("乐税服务费提现申请，没有查询到serviceAccount，创建。。。账户类型：{}，用户ID：{}", accountType, idCode);
        serviceAccountParam.setVersion(0);
        serviceAccountParam.setBalance(BigDecimal.ZERO);
        this.save(serviceAccountParam);
        return serviceAccountParam;
    }

    /**
     * 冻结金额
     *
     * @param accountType 账户类型
     * @param idCode      idCode
     * @param profitType  收益类型
     * @param amount      金额
     * @param bizId       提现申请ID
     * @param cycleAddId  提现周期ID
     */
    @Override
    public void freezeBalance(Integer accountType, Integer idCode, ProfitTypeEnum profitType, BigDecimal amount, Integer bizId, Integer cycleAddId) {
        //事务在上层service加
        ServiceAccount serviceAccount = this.getAccount(accountType, idCode);

        BigDecimal freezeBalance = serviceAccount.getFreezeBalance().add(amount);
        ServiceAccount serviceAccountUp = new ServiceAccount();
        BigDecimal balance = serviceAccount.getBalance().subtract(amount);
        serviceAccountUp.setBalance(balance);
        serviceAccountUp.setFreezeBalance(freezeBalance);
        serviceAccountUp.setVersion(serviceAccount.getVersion() + 1);

        ServiceAccount serviceAccountWhere = new ServiceAccount();
        serviceAccountWhere.setId(serviceAccount.getId());
        serviceAccountWhere.setVersion(serviceAccount.getVersion());
        boolean updateSuccess = this.update(serviceAccountUp, new UpdateWrapper<>(serviceAccountWhere));
        Assert.isTrue(updateSuccess, "更新服务费账户失败");

        ServiceFrozenDetail serviceFrozenDetail = new ServiceFrozenDetail();
        serviceFrozenDetail.setAccountId(serviceAccount.getId());
        serviceFrozenDetail.setBizCode(profitType.toString());
        serviceFrozenDetail.setBizId(bizId);
        serviceFrozenDetail.setCycleAddId(cycleAddId);
        serviceFrozenDetail.setCreatedTime(LocalDateTime.now());
        serviceFrozenDetail.setFreezeAmount(amount);
        serviceFrozenDetail.setIdCode(idCode);
        serviceFrozenDetail.setStatus(CoverChargeEnum.FreezeStatus.FREEZE.getCode());
        serviceFrozenDetail.setCreatedBy(idCode + "");

        ServiceFrozenDetail serviceFrozenDetail1Where = new ServiceFrozenDetail();
        serviceFrozenDetail1Where.setBizId(bizId);
        serviceFrozenDetail1Where.setBizCode(profitType.toString());
        boolean result = serviceFrozenDetailService.saveOrUpdate(serviceFrozenDetail, new UpdateWrapper<>(serviceFrozenDetail1Where));
        Assert.isTrue(result, "更新冻结记录失败");
    }

    /**
     * 批量解冻金额
     *
     * @param accountType             账户类型
     * @param idCode                  idCode
     * @param profitType              收益类型
     * @param totalAmount             金额
     * @param withdrawalApplyInfoList 提现申请列表
     */
    @Override
    public void unFreezeBalance(Integer accountType, Integer idCode, ProfitTypeEnum profitType, BigDecimal totalAmount, List<WithdrawalApplyInfo> withdrawalApplyInfoList) {
        //事务在上层service加
        ServiceAccount serviceAccount = this.getAccount(accountType, idCode);

        BigDecimal freezeBalance = serviceAccount.getFreezeBalance().subtract(totalAmount);
        ServiceAccount serviceAccountUp = new ServiceAccount();
        serviceAccountUp.setBalance(serviceAccount.getBalance().add(totalAmount));//解冻可用金额要加回去
        serviceAccountUp.setFreezeBalance(freezeBalance);
        serviceAccountUp.setVersion(serviceAccount.getVersion() + 1);

        ServiceAccount serviceAccountWhere = new ServiceAccount();
        serviceAccountWhere.setId(serviceAccount.getId());
        serviceAccountWhere.setVersion(serviceAccount.getVersion());
        boolean updateSuccess = this.update(serviceAccountUp, new UpdateWrapper<>(serviceAccountWhere));
        Assert.isTrue(updateSuccess, "更新服务费账户失败");

        LocalDateTime now = LocalDateTime.now();
        for (WithdrawalApplyInfo withdrawalApplyInfo : withdrawalApplyInfoList) {
            ServiceFrozenDetail serviceFrozenDetail = new ServiceFrozenDetail();
            serviceFrozenDetail.setUpdatedTime(now);
//            serviceFrozenDetail.setFreezeAmount(withdrawalApplyInfo.getApplyMoney());
            serviceFrozenDetail.setUpdatedBy(idCode + "");
            serviceFrozenDetail.setStatus(CoverChargeEnum.FreezeStatus.UN_FREEZE.getCode());

            ServiceFrozenDetail serviceFrozenDetail1Where = new ServiceFrozenDetail();
            serviceFrozenDetail1Where.setBizId(withdrawalApplyInfo.getBizId());
            serviceFrozenDetail1Where.setBizCode(profitType.toString());

            boolean update = serviceFrozenDetailService.update(serviceFrozenDetail, new UpdateWrapper<>(serviceFrozenDetail1Where));
            Assert.isTrue(update, "更新冻结记录失败");
        }

    }

    /**
     * 服务费发放成功  减去账户冻结金额
     *
     * @param accountId
     * @param amount
     * @param profitType OperateTypeEnum 枚举类内相关
     * @param bizId      OperateTypeEnum  枚举类内相关desc
     */
    @Override
    public void updateBalanceForSuccess(Integer accountId, BigDecimal amount, ProfitTypeEnum profitType, Integer bizId, Integer cycleAddId) {
        //修改收益账户金额
        ServiceAccount serviceAccount = this.getById(accountId);
        BigDecimal freezeBalance = serviceAccount.getFreezeBalance().subtract(amount);
        ServiceAccount serviceAccountUp = new ServiceAccount();
        serviceAccountUp.setFreezeBalance(freezeBalance);
        serviceAccountUp.setVersion(serviceAccount.getVersion() + 1);

        ServiceAccount serviceAccountWhere = new ServiceAccount();
        serviceAccountWhere.setId(serviceAccount.getId());
        serviceAccountWhere.setVersion(serviceAccount.getVersion());
        boolean updateSuccess = this.update(serviceAccountUp, new UpdateWrapper<>(serviceAccountWhere));
        if (!updateSuccess) {
            throw new ServiceException("更新服务费账户失败");
        }

        ServiceFrozenDetail serviceFrozenDetail = new ServiceFrozenDetail();
        serviceFrozenDetail.setUpdatedTime(LocalDateTime.now());
        serviceFrozenDetail.setUpdatedBy(serviceAccount.getIdCode() + "");
        serviceFrozenDetail.setStatus(CoverChargeEnum.FreezeStatus.UN_FREEZE.getCode());

        ServiceFrozenDetail serviceFrozenDetail1Where = new ServiceFrozenDetail();
        if (cycleAddId != null) {
            serviceFrozenDetail1Where.setCycleAddId(cycleAddId);
        } else {
            serviceFrozenDetail1Where.setBizId(bizId);
        }
        serviceFrozenDetail1Where.setBizCode(profitType.toString());

        boolean update = serviceFrozenDetailService.update(serviceFrozenDetail, new UpdateWrapper<>(serviceFrozenDetail1Where));
        Assert.isTrue(update, "更新冻结记录失败");

//        if (CoverChargeEnum.ServiceAccountType.CUSTOMER_TYPE.getCode().equals(serviceAccount.getAccountType())) {
//            ServiceCustomDetail serviceCustomDetail = new ServiceCustomDetail();
//            serviceCustomDetail.setIdCode(serviceAccount.getIdCode());
//            serviceCustomDetail.setProfitType(profitType.getCode());
//            serviceCustomDetail.setDes(profitType.getDesc());
//            serviceCustomDetail.setAmount(amount);
//            serviceCustomDetail.setBalance(serviceAccount.getBalance());
//            serviceCustomDetail.setFreezeBalance(freezeBalance);
//            serviceCustomDetail.setTableId(bizId);
//            serviceCustomDetailService.save(serviceCustomDetail);
//        } else
        ServiceManageDetail serviceManageDetail = new ServiceManageDetail();
        serviceManageDetail.setIdCode(serviceAccount.getIdCode());
        serviceManageDetail.setProfitType(profitType.getCode());
        serviceManageDetail.setDes(profitType.getDesc());
        serviceManageDetail.setAmount(amount);
        serviceManageDetail.setBalance(serviceAccount.getBalance());
        serviceManageDetail.setFreezeBalance(freezeBalance);
        serviceManageDetail.setTableId(bizId);
        serviceManageDetailService.save(serviceManageDetail);
    }

    /**
     * 服务费发放失败  账户冻结金额回滚
     *
     * @param accountId
     * @param amount
     * @param profitType OperateTypeEnum 枚举类内相关
     * @param bizId      OperateTypeEnum  枚举类内相关desc
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackFreezeBalance(AccountId accountId, Amount amount, ProfitTypeEnum profitType, Integer bizId, Integer cycleAddId) {
        //账户冻结金额回滚
        Account account = accountRepository.findByAccountId(accountId);
        Integer oldVersion = account.getVersion();
        account.rollbackFreezeBalance(amount);
        accountRepository.updateBalance(account, oldVersion);

        //冻结金额流水置为解冻状态
        if (cycleAddId != null) {
            frozenDetailRepository.updateStatusByCycleAddIdBizCode(FrozenStatus.UNFREEZE,
                    account.getIdCode(), cycleAddId, profitType.toString());
        } else {
            frozenDetailRepository.updateStatusByBizIdBizCode(FrozenStatus.UNFREEZE,
                    account.getIdCode(), bizId, profitType.toString());
        }
    }

    @Override
    public void plusBalance(AccountId accountId, Amount amount) {
        Account account = accountRepository.findByAccountId(accountId);
        Integer oldVersion = account.getVersion();
        account.plusBalance(amount);
        accountRepository.updateBalance(account, oldVersion);
    }


    /**
     * 收益账户余额查询
     */
    @Override
    public Page<AdminServiceAccountQueryVo> queryPageByIdCode(Page page, String idCode, String mobile) {
        UserInfo userVO = null;
        if (!StringUtils.isBlank(mobile)) {
            try {
                userVO = userDocService.searchUserByIdCodeOrMobile(null, mobile);
            } catch (Exception e) {
                log.error("通过手机号获取用户信息异常:", e);
                throw new ServiceException("会员服务异常");
            }
            //条件不存在情况
            if (StringUtils.isBlank(idCode) && Objects.isNull(userVO)) {
                return page.setRecords(Lists.newArrayList());
            }
        }
        log.info("通过手机号获取用户信息:{}", userVO);
        //idCode 和手机号不为空的情况
        if (!StringUtils.isBlank(idCode) && !Objects.isNull(userVO)) {
            //如果输入idCode跟手机号不一致
            if (!idCode.equals(String.valueOf(userVO.getIdCode()))) {
                return page.setRecords(Lists.newArrayList());
            }
        }
        //最终按照idCode获取数据
        if (!Objects.isNull(userVO)) {
            idCode = String.valueOf(userVO.getIdCode());
        }
        List<AdminServiceAccountQueryVo> vos = this.baseMapper.queryPageByIdCode(page, idCode);
        List<UserIdCode> idList = new ArrayList<>();
        for (AdminServiceAccountQueryVo accountQueryVo : vos) {
            idList.add(new UserIdCode(accountQueryVo.getIdCode()));
        }
        Map<UserIdCode, UserInfo> userMap = userService.queryUserMap(idList);
        vos.forEach(vo -> {

            vo.setName(Optional.ofNullable(userMap.get(new UserIdCode(vo.getIdCode()))).map(UserInfo::getName).orElse(""));
            vo.setMobile(Optional.ofNullable(userMap.get(new UserIdCode(vo.getIdCode()))).map(UserInfo::getMobile).orElse(""));
        });
        page.setRecords(vos);
        return page;
    }

    /**
     * 获取账户信息
     */
    @Override
    public List<ServiceAccountDTO> getAccountInfoByIdCodes(QueryAccountReqDTO req) {
        List<ServiceAccountDTO> lists = this.baseMapper.getAccountInfoByIdCodes(req.getIdCodeList(),
                Objects.isNull(req.getAccountType()) ? null : req.getAccountType().getCode());
        return lists;
    }

    /**
     * 获取账户信息
     */
    @Override
    public ServiceAccountDTO batchGetBalanceByIdCodeAndType(QueryAccountReqDTO req) {
        ServiceAccountDTO serviceAccountDTO = this.baseMapper.batchGetBalanceByIdCodeAndType(req.getIdCode(),
                Objects.isNull(req.getAccountType()) ? null : req.getAccountType().getCode());
        return serviceAccountDTO;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean dealServiceAccountAndDetail(ChangeProfitDTO changeProfit) {
        if (Objects.isNull(changeProfit.getSettleProfit()) && Objects.isNull(changeProfit.getCancelOrReturnProfit())) {
            log.info("结算收益数据处理,无数据,不处理");
            return Boolean.TRUE;
        }
        //结算收益数据
        if (!Objects.isNull(changeProfit.getSettleProfit()) && Objects.isNull(changeProfit.getCancelOrReturnProfit())) {
            log.info("结算收益数据处理");
            ProfitDataDTO profitData = changeProfit.getSettleProfit();
            dealProfitAccountData(profitData);
            dealProfitData(profitData);
            return Boolean.TRUE;
        }
        //取消或退款收益数据
        if (Objects.isNull(changeProfit.getSettleProfit()) && !Objects.isNull(changeProfit.getCancelOrReturnProfit())) {
            log.info("取消或退款收益数据处理");
            ProfitDataDTO profitData = changeProfit.getCancelOrReturnProfit();
            dealProfitAccountData(profitData);
            dealProfitData(profitData);
            return Boolean.TRUE;
        }
        log.info("先结算收益数据处理->取消或退款收益数据处理");
        //先结算收益数据->取消或退款收益数据
        dealProfitAccountData(changeProfit.getSettleProfit(), changeProfit.getCancelOrReturnProfit());
        return Boolean.TRUE;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void dealServiceAccountByDetail(String uniqueId, ChangeProfitDetailDTO changeProfit) {
        if (CollectionUtils.isEmpty(changeProfit.getSettleProfit()) && CollectionUtils.isEmpty(changeProfit.getCancelOrReturnProfit())) {
            log.info("根据明细处理结算收益数据,无明细数据,不处理");
            return;
        }
        if (!CollectionUtils.isEmpty(changeProfit.getSettleProfit())) {
            log.info("根据明细处理结算收益数据");
            List<ProfitDetailDataDTO> settleProfit = changeProfit.getSettleProfit();
            dealAccountAndDetailByDetail(uniqueId, settleProfit);
        }
        if (!CollectionUtils.isEmpty(changeProfit.getCancelOrReturnProfit())) {
            log.info("根据明细处理取消或退款收益数据");
            List<ProfitDetailDataDTO> cancelProfit = changeProfit.getCancelOrReturnProfit();
            dealAccountAndDetailByDetail(uniqueId, cancelProfit);
        }
    }

    /**
     * 订正账户数据
     *
     * @param req 订正数据
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void revisionAccountData(AccountRevisionApplyReqVo req) {
        //校验账户是否存在
        QueryWrapper<ServiceAccount> queryAccount = new QueryWrapper<>();
        queryAccount.eq("id_code", req.getIdCode());
        queryAccount.eq("account_type", req.getAccountType());
        ServiceAccount serviceAccount = this.getOne(queryAccount);
        if (null == serviceAccount) {
            log.info("[数据修复][账户数据修复失败][该账户不存在,idCode:{}]", req.getIdCode());
            throw new ServiceException("该账户不存在");
        }
        BigDecimal balance;
        //校验订正类型是否存在
        QueryWrapper<ServiceProfitType> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("type", req.getProfitType());
        ServiceProfitType serviceProfitType = serviceProfitTypeService.getOne(queryWrapper);
        if (null == serviceProfitType) {
            throw new ServiceException("业务类型不存在");
        }
        //校验是收益还是扣除
        if (ModifyTypeEnum.EXPEND.getCode().equals(serviceProfitType.getModifyType())) {
            balance = serviceAccount.getBalance().subtract(req.getMoney());
        } else {
            balance = serviceAccount.getBalance().add(req.getMoney());
        }
        ServiceAccount updateServiceAccount = new ServiceAccount();
        updateServiceAccount.setBalance(balance);
        updateServiceAccount.setVersion(serviceAccount.getVersion() + 1);
        ServiceAccount serviceAccountWhere = new ServiceAccount();
        serviceAccountWhere.setId(serviceAccount.getId());
        serviceAccountWhere.setVersion(serviceAccount.getVersion());
        boolean updateSuccess = this.update(updateServiceAccount, new QueryWrapper<>(serviceAccountWhere));
        if (!updateSuccess) {
            throw new ServiceException("更新服务费账户失败");
        }
//        if (CoverChargeEnum.ServiceAccountType.CUSTOMER_TYPE.getCode().equals(serviceAccount.getAccountType())) {
//            ServiceCustomDetail serviceCustomDetail = new ServiceCustomDetail();
//            serviceCustomDetail.setIdCode(serviceAccount.getIdCode());
//            serviceCustomDetail.setProfitType(req.getProfitType());
//            serviceCustomDetail.setDes(req.getProfitDes());
//            serviceCustomDetail.setAmount(req.getMoney());
//            serviceCustomDetail.setBalance(balance);
//            serviceCustomDetail.setFreezeBalance(serviceAccount.getFreezeBalance());
//            serviceCustomDetail.setTableId(req.getTableId());
//            //判断是否是订单修复
//            if (RevisionIsOrderEnum.IS_ORDER.getCode().equals(req.getIsOrder())) {
//                serviceCustomDetail.setOrderId(req.getOrderId());
//                serviceCustomDetail.setGoodsId(req.getGoodsId());
//                serviceCustomDetail.setReturnId(req.getReturnId());
//            }
//            serviceCustomDetailService.save(serviceCustomDetail);
//        } else
        ServiceManageDetail serviceManageDetail = new ServiceManageDetail();
        serviceManageDetail.setIdCode(serviceAccount.getIdCode());
        serviceManageDetail.setProfitType(req.getProfitType());
        serviceManageDetail.setDes(req.getProfitDes());
        serviceManageDetail.setAmount(req.getMoney());
        serviceManageDetail.setBalance(serviceAccount.getBalance());
        serviceManageDetail.setFreezeBalance(serviceAccount.getFreezeBalance());
        serviceManageDetail.setTableId(req.getTableId());
        //判断是否是订单修复
        if (RevisionIsOrderEnum.IS_ORDER.getCode().equals(req.getIsOrder())) {
            serviceManageDetail.setOrderId(req.getOrderId());
            serviceManageDetail.setGoodsId(req.getGoodsId());
            serviceManageDetail.setReturnId(req.getReturnId());
        }
        serviceManageDetailService.save(serviceManageDetail);


    }

    /**
     * 处理收益账户数据
     *
     * @param profitData 数据
     */
    private void dealProfitAccountData(ProfitDataDTO profitData) {
        List<ServiceAccountDTO> accountList = profitData.getServiceAccountDTOList();
        if (CollectionUtils.isEmpty(accountList)) {
            return;
        }
        for (ServiceAccountDTO account : accountList) {
            ServiceAccount accountInfo = BeanMapperUtils.map(account, ServiceAccount.class);
            if (Objects.isNull(accountInfo)) {
                log.error("ServiceAccount数据转换异常,account:{}", account);
                throw new ServiceException("账户数据转换异常");
            }
            boolean result = false;
            if (Objects.isNull(accountInfo.getVersion())) {
                result = this.save(accountInfo);
            } else {
                ServiceAccount serviceAccountUp = new ServiceAccount();
                serviceAccountUp.setBalance(accountInfo.getBalance());
                serviceAccountUp.setVersion(accountInfo.getVersion() + 1);

                ServiceAccount serviceAccountWhere = new ServiceAccount();
                serviceAccountWhere.setIdCode(accountInfo.getIdCode());
                serviceAccountWhere.setVersion(accountInfo.getVersion());
                result = this.update(serviceAccountUp, new UpdateWrapper<>(serviceAccountWhere));
            }
            if (!result) {
                log.error("服务费账户数据处理失败，account：{}", account);
                throw new ServiceException("服务费账户数据处理失败");
            }
        }
    }

    /**
     * 处理收益账户数据
     *
     * @param settleProfit         结算收益
     * @param cancelOrReturnProfit 取消结算收益
     */
    private void dealProfitAccountData(ProfitDataDTO settleProfit, ProfitDataDTO cancelOrReturnProfit) {
        dealProfitAccountData(settleProfit);
        dealProfitData(settleProfit);
        List<ServiceAccountDTO> accountList = cancelOrReturnProfit.getServiceAccountDTOList();
        if (CollectionUtils.isEmpty(accountList)) {
            return;
        }
        List<ServiceAccountDTO> settleAccount = settleProfit.getServiceAccountDTOList();
        for (ServiceAccountDTO account : accountList) {
            ServiceAccount accountInfo = BeanMapperUtils.map(account, ServiceAccount.class);
            if (Objects.isNull(accountInfo)) {
                log.error("ServiceAccount数据转换异常,account:{}", account);
                throw new ServiceException("账户数据转换异常");
            }
            boolean result = false;
            if (Objects.isNull(accountInfo.getVersion())) {
                result = this.save(accountInfo);
            } else {
                Integer version = accountInfo.getVersion();
                //同一个事物中，账户操作多次
                if (!CollectionUtils.isEmpty(settleAccount)) {
                    long count = settleAccount.stream().filter(info -> info.getIdCode().equals(account.getIdCode())
                            && info.getAccountType().equals(account.getAccountType())).count();
                    if (count > 0) {
                        version = version + 1;
                    }
                }

                ServiceAccount serviceAccountUp = new ServiceAccount();
                serviceAccountUp.setBalance(accountInfo.getBalance());
                serviceAccountUp.setVersion(version + 1);

                ServiceAccount serviceAccountWhere = new ServiceAccount();
                serviceAccountWhere.setIdCode(accountInfo.getIdCode());
                serviceAccountWhere.setVersion(version);
                result = this.update(serviceAccountUp, new UpdateWrapper<>(serviceAccountWhere));
            }
            if (!result) {
                log.error("服务费账户数据处理失败，account：{}", account);
                throw new ServiceException("服务费账户数据处理失败");
            }
        }
        dealProfitData(cancelOrReturnProfit);
    }

    /**
     * 处理收益数据
     *
     * @param profitData 收益
     * @return
     */
    private void dealProfitData(ProfitDataDTO profitData) {
        List<ServiceCustomDetailDTO> customDetailList = profitData.getServiceCustomDetailDTOList();
        addCustomDetail(customDetailList);
        List<ServiceManageDetailDTO> manageDetailList = profitData.getServiceManageDetailDTOList();
        addManagerDetail(manageDetailList);
    }


    /**
     * ABM客户收益账户数据处理
     *
     * @param customDetailList 数据
     */
    private void addCustomDetail(List<ServiceCustomDetailDTO> customDetailList) {
        if (CollectionUtils.isEmpty(customDetailList)) {
            return;
        }
        customDetailList.forEach(info -> {
            ServiceCustomDetail customDetail = BeanMapperUtils.map(info, ServiceCustomDetail.class);
            if (Objects.isNull(customDetail)) {
                log.error("ServiceCustomDetail数据转换异常,info:{}", info);
                throw new ServiceException("ABM客户收益账户数据转换异常");
            }
            customDetail.setCreatedAt(LocalDateTime.now());
            customDetail.setUpdatedAt(LocalDateTime.now());
            boolean result = serviceCustomDetailService.save(customDetail);
            if (!result) {
                log.error("ABM客户收益账户数据处理失败，info：{}", info);
                throw new ServiceException("ABM客户收益账户数据处理失败");
            }
        });
    }


    /**
     * DT管理收益账户数据处理
     *
     * @param manageDetailList 数据
     */
    private void addManagerDetail(List<ServiceManageDetailDTO> manageDetailList) {
        if (CollectionUtils.isEmpty(manageDetailList)) {
            return;
        }
        manageDetailList.forEach(info -> {
            ServiceManageDetail manageDetail = BeanMapperUtils.map(info, ServiceManageDetail.class);
            if (Objects.isNull(manageDetail)) {
                log.error("ServiceManageDetail数据转换异常,info:{}", info);
                throw new ServiceException("DT管理收益账户数据转换异常");
            }
            manageDetail.setCreatedAt(LocalDateTime.now());
            manageDetail.setUpdatedAt(LocalDateTime.now());
            boolean result = serviceManageDetailService.save(manageDetail);
            if (!result) {
                log.error("DT管理收益账户数据处理失败，info：{}", info);
                throw new ServiceException("DT管理收益账户数据处理失败");
            }
        });
    }


    /**
     * 根据详情处理账户跟明细记录
     *
     * @param profitDetails 详情记录
     */
    private void dealAccountAndDetailByDetail(String uniqueId, List<ProfitDetailDataDTO> profitDetails) {
        if (CollectionUtils.isEmpty(profitDetails)) {
            return;
        }
        //根据账务类型
        Map<ServiceAccountTypeEnum, List<ProfitDetailDataDTO>> typeResultMap = profitDetails.stream()
                .collect(Collectors.groupingBy(ProfitDetailDataDTO::getAccountType));
        for (Map.Entry<ServiceAccountTypeEnum, List<ProfitDetailDataDTO>> typeEntry : typeResultMap.entrySet()) {
            ServiceAccountTypeEnum typeKey = typeEntry.getKey();
            List<ProfitDetailDataDTO> profitDetailList = typeEntry.getValue();
            //根据用户分组
            Map<Integer, List<ProfitDetailDataDTO>> idCodeResultMap = profitDetailList.stream()
                    .collect(Collectors.groupingBy(ProfitDetailDataDTO::getIdCode));
            dealProfitDetailByIdCode(uniqueId, typeKey, idCodeResultMap);
        }
    }

    /**
     * 根据用户分组后处理账户跟明细记录
     *
     * @param typeKey         账户类型
     * @param idCodeResultMap 用户分组结果
     */
    private void dealProfitDetailByIdCode(String uniqueId, ServiceAccountTypeEnum typeKey, Map<Integer, List<ProfitDetailDataDTO>> idCodeResultMap) {
        if (CollectionUtils.isEmpty(idCodeResultMap)) {
            return;
        }
        for (Map.Entry<Integer, List<ProfitDetailDataDTO>> idCodeEntry : idCodeResultMap.entrySet()) {
            Integer idCode = idCodeEntry.getKey();
            List<ProfitDetailDataDTO> opDetailList = idCodeEntry.getValue();
            //收益变更类型分组
            Map<OperateTypeEnum, List<ProfitDetailDataDTO>> opTypeResultMap = opDetailList.stream()
                    .collect(Collectors.groupingBy(ProfitDetailDataDTO::getOpType));
            if (CollectionUtils.isEmpty(opTypeResultMap)) {
                return;
            }
            //获取用户账户
            ServiceAccount serviceAccount = getAccount(typeKey.getCode(), idCode);
            BigDecimal accountAmt = serviceAccount.getBalance();
            BigDecimal freezeBalance = Objects.isNull(serviceAccount.getFreezeBalance()) ? BigDecimal.ZERO : serviceAccount.getFreezeBalance();
            log.info("获取到服务费账户信息：{}", serviceAccount);
            for (Map.Entry<OperateTypeEnum, List<ProfitDetailDataDTO>> opTypeEntry : opTypeResultMap.entrySet()) {
                OperateTypeEnum operateTypeEnum = opTypeEntry.getKey();
                List<ProfitDetailDataDTO> detailList = opTypeEntry.getValue();
                //金额累计
                BigDecimal amountSum = detailList.stream().filter(info -> !Objects.isNull(info.getAmount()))
                        .map(ProfitDetailDataDTO::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                amountSum = amountConvert(operateTypeEnum, amountSum);

                Integer version = serviceAccount.getVersion();

                ServiceAccount serviceAccountUp = new ServiceAccount();
                serviceAccountUp.setBalance(serviceAccount.getBalance().add(amountSum));
                serviceAccountUp.setVersion(version + 1);

                ServiceAccount serviceAccountWhere = new ServiceAccount();
                serviceAccountWhere.setIdCode(idCode);
                serviceAccountWhere.setVersion(version);
                boolean result = this.update(serviceAccountUp, new UpdateWrapper<>(serviceAccountWhere));
                if (!result) {
                    log.error("服务费账户数据处理失败，account：{}", serviceAccount);
                    throw new ServiceException("服务费账户数据处理失败");
                }
                switch (typeKey) {
                    case CUSTOMER_TYPE:
                        addCustomDetailByType(accountAmt, freezeBalance, detailList);
                        break;
                    case MANAGE_TYPE:
                        addManagerDetailByType(uniqueId, accountAmt, freezeBalance, detailList);
                        break;
                    default:
                        break;
                }
            }
        }
    }


    /**
     * ABM客户收益账户数据处理
     *
     * @param accountAmt       账户余额
     * @param customDetailList 明细
     */
    private void addCustomDetailByType(BigDecimal accountAmt, BigDecimal freezeBalance, List<ProfitDetailDataDTO> customDetailList) {
        if (CollectionUtils.isEmpty(customDetailList)) {
            return;
        }
        for (ProfitDetailDataDTO info : customDetailList) {
            ServiceCustomDetail customDetail = BeanMapperUtils.map(info, ServiceCustomDetail.class);
            if (Objects.isNull(customDetail)) {
                log.error("ServiceCustomDetail数据转换异常,info:{}", info);
                throw new ServiceException("ABM客户收益账户数据转换异常");
            }
            BigDecimal amount = amountConvert(info.getOpType(), info.getAmount());
            customDetail.setAmount(amount);
            accountAmt = accountAmt.add(amount);
            customDetail.setBalance(accountAmt);
            customDetail.setFreezeBalance(freezeBalance);
            customDetail.setProfitType(info.getProfitTypeEnum().getCode());

            customDetail.setCreatedAt(LocalDateTime.now());
            customDetail.setUpdatedAt(LocalDateTime.now());
            boolean result = serviceCustomDetailService.save(customDetail);
            if (!result) {
                log.error("ABM客户收益账户数据处理失败，info：{}", info);
                throw new ServiceException("ABM客户收益账户数据处理失败");
            }
        }
    }


    /**
     * DT管理收益账户数据处理
     *
     * @param manageDetailList 数据
     */
    private void addManagerDetailByType(String uniqueId, BigDecimal accountAmt, BigDecimal freezeBalance, List<ProfitDetailDataDTO> manageDetailList) {
        if (CollectionUtils.isEmpty(manageDetailList)) {
            return;
        }
        for (ProfitDetailDataDTO info : manageDetailList) {
            ServiceManageDetail manageDetail = BeanMapperUtils.map(info, ServiceManageDetail.class);
            if (Objects.isNull(manageDetail)) {
                log.error("ServiceManageDetail数据转换异常,info:{}", info);
                throw new ServiceException("DT管理收益账户数据转换异常");
            }
            StringBuilder bizIdBuilder = new StringBuilder();
            if (OperateTypeEnum.REFUND == info.getOpType()) {
                bizIdBuilder.append("RE");
            } else if (OperateTypeEnum.SETTLE == info.getOpType()) {
                bizIdBuilder.append("SE");
            } else {
                throw new ServiceException("服务费操作类型异常");
            }
            String bizId = bizIdBuilder.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime()))
                    .append(uniqueId)
                    .append(String.format("%04d", info.getProfitTypeEnum().getCode())).toString();

            manageDetail.setBizId(bizId);
            manageDetail.setProfitType(info.getProfitTypeEnum().getCode());
            BigDecimal amount = amountConvert(info.getOpType(), info.getAmount());
            manageDetail.setAmount(amount);
            accountAmt = accountAmt.add(amount);
            manageDetail.setBalance(accountAmt);
            manageDetail.setFreezeBalance(freezeBalance);

            manageDetail.setCreatedAt(LocalDateTime.now());
            manageDetail.setUpdatedAt(LocalDateTime.now());
            boolean result = serviceManageDetailService.save(manageDetail);
            if (!result) {
                log.error("DT管理收益账户数据处理失败，info：{}", info);
                throw new ServiceException("DT管理收益账户数据处理失败");
            }
        }
    }

    /**
     * 金额转换
     */
    private BigDecimal amountConvert(OperateTypeEnum operateTypeEnum, BigDecimal amountSum) {
        if (Objects.isNull(amountSum)) {
            return BigDecimal.ZERO;
        }
        if (Objects.isNull(operateTypeEnum)) {
            return amountSum;
        }
        switch (operateTypeEnum) {
            case SETTLE:
                break;
            case REFUND:
                amountSum = amountSum.multiply(new BigDecimal(-1));
                break;
            default:
                break;
        }
        return amountSum;
    }

    /**
     * 获取收益账户-若不存在则返回空
     *
     * @param accountType 账户类型
     * @param idCode      idCode
     */
    @Override
    public ServiceAccount judgeAccountExis(Integer accountType, Integer idCode) {
        ServiceAccount serviceAccountParam = new ServiceAccount();
        serviceAccountParam.setIdCode(idCode);
        ServiceAccount serviceAccount = this.getOne(new QueryWrapper<>(serviceAccountParam));
        return serviceAccount;
    }

}
